package com.ctrip.payment.service.base;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ctrip.payment.domain.PageParamer;
import com.ctrip.payment.domain.base.BaseJpaEntity;
import com.ckjava.xutils.ArrayUtils;
import com.ckjava.xutils.Constants;
import com.ckjava.xutils.DateUtils;
import com.ckjava.xutils.http.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.util.*;

@Transactional(readOnly = true)
public abstract class BaseJpaCrudService<E extends BaseJpaEntity> implements Constants {

    private static Logger logger = LoggerFactory.getLogger(BaseJpaCrudService.class);

    @PersistenceContext
    protected EntityManager entityManager;

    /**
     * 获取子类的 Class 类型
     * @return
     */
    public abstract Class<E> getClassType();

    /**
     * 获取到 List 后对 List 中的数据进行处理 <br>
     *
     * 子类实现重写该方法，实现对 getPage findAll search 返回的 List 中的对象进行处理<br>
     *  如果不需要处理，直接返回参数 dataList
     *
     * @param dataList
     * @return
     */
    protected List<E> afterList(Optional<List<E>> dataList){
        return dataList.get();
    }

    /**
     * 获取到 对象后对 对象中的元素进行处理<br>
     *
     * 子类实现重写该方法，实现对 get 方法返回的对象进行处理<br>
     * 如果不需要处理，直接返回参数 e
     *
     * @param entity
     * @return
     */
    protected E afterEntity(Optional<E> entity) {
        return entity.get();
    }

    /**
     * 删除实体后的处理
     *
     * @param entity
     * @return
     */
    protected E afterDelete(E entity) {
        return entity;
    }

    /**
     * 删除实体前的处理
     *
     * @param entity
     * @return
     */
    protected E beforeDelete(E entity) {
        return entity;
    }

    /**
     * 保存实体前的处理
     *
     * @param entity
     * @return
     */
    protected E beforeSave(E entity) {
        return entity;
    }

    /**
     * 修改实体前的处理
     *
     * @param entity
     * @return
     */
    protected E beforeUpdate(E entity) {
        return entity;
    }

    /**
     * 保存实体后的处理
     *
     * @param entity
     * @return
     */
    protected E afterSave(E entity) {
        return entity;
    }

    /**
     * 修改实体后的处理
     *
     * @param entity
     * @return
     */
    protected E afterUpdate(E entity) {
        return entity;
    }

    /**
     * 根据 id 查询一个对象
     * @param id
     * @return
     */
    public Optional<E> get(Long id) {
        Optional<E> optionalEntity = Optional.ofNullable(entityManager.find(getClassType(), id));
        optionalEntity.ifPresent(data -> afterEntity(optionalEntity));
        return optionalEntity;
    }

    /**
     * 根据关键字搜索对象
     * @param keyword
     * @return
     */
    public Optional<List<E>> search(String keyword) {
        try {
            CriteriaBuilder cb = entityManager.getCriteriaBuilder();
            CriteriaQuery<E> q = cb.createQuery(getClassType());
            Root<E> c = q.from(getClassType());
            q.select(c);

            // 获取类的所有属性名称
            String jsonString = JSONObject.toJSONString(getClassType().newInstance(), SerializerFeature.WriteMapNullValue);
            Map<String, String> pro = JSONObject.toJavaObject(JSONObject.parseObject(jsonString), Map.class);

            List<Predicate> orList = new ArrayList<>();
            pro.forEach((key,value) -> {
                Path path = c.get(key);
                if (path != null) {
                    Class clzz = path.getJavaType();
                    if (clzz.equals(String.class)) {
                        orList.add(cb.like(path, "%" + keyword + "%"));
                    }
                }
            });

            // 加上默认条件 和 所有 or 的条件
            q.where(cb.and(cb.equal(c.get("delFlag"), BaseJpaEntity.DEL_NO)),
                    cb.or(orList.toArray(new Predicate[orList.size()])));

            List<E> dataList = afterList(Optional.ofNullable(entityManager.createQuery(q).getResultList()));

            return Optional.ofNullable(dataList);
        } catch (Exception e) {
            logger.error(this.getClass().getName().concat(".search getClassType().newInstance() has error"), e);
            return Optional.empty();
        }
    }

    /**
     * 获取所有
     * @return
     */
    public Optional<List<E>> findAll() {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<E> q = cb.createQuery(getClassType());
        Root<E> c = q.from(getClassType());
        q.select(c);
        // 加上默认条件
        q.where(cb.equal(c.get("delFlag"), BaseJpaEntity.DEL_NO));

        List<E> dataList = afterList(Optional.ofNullable(entityManager.createQuery(q).getResultList()));

        return Optional.ofNullable(dataList);
    }

    /**
     * 根据指定条件获取 List 集合
     *
     * @return Optional<List<E>>
     */
    public <V> Optional<List<E>> findBy(Map<String, V> byMap) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<E> q = cb.createQuery(getClassType());
        Root<E> c = q.from(getClassType());
        q.select(c);

        // 加上默认条件
        List<Predicate> whereList = new ArrayList<>();
        whereList.add(cb.equal(c.get("delFlag"), BaseJpaEntity.DEL_NO));
        // 加上其他条件
        byMap.forEach((key, value) -> {

            Path path = c.get(key);
            if (path != null) {
                if (path.getJavaType().equals(String.class)) {
                    whereList.add(cb.equal(c.get(key), value));
                } else if (path.getJavaType().equals(Date.class)) {
                    String[] dates = value.toString().split(SPLITER.COMMA);
                    Date startDate = DateUtils.parseDate(ArrayUtils.getValue(dates, 0));
                    Date endDate = DateUtils.parseDate(ArrayUtils.getValue(dates, 1));
                    whereList.add(cb.greaterThanOrEqualTo(c.get(key), startDate));
                    whereList.add(cb.lessThanOrEqualTo(c.get(key), endDate));
                } else {
                    whereList.add(cb.equal(c.get(key), value));
                }
            }

        });
        q.where(whereList.toArray(new Predicate[whereList.size()]));
        List<E> dataList = afterList(Optional.ofNullable(entityManager.createQuery(q).getResultList()));
        return Optional.ofNullable(dataList);
    }

    /**
     * 分页获取数据
     * @param pageParamer
     * @return Optional<Page<E>>
     */
    public Optional<Page<E>> getPage(PageParamer pageParamer) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<E> q = cb.createQuery(getClassType());
        Root<E> c = q.from(getClassType());
        q.select(c);

        List<Predicate> whereList = new ArrayList<>();
        // 默认条件
        whereList.add(cb.equal(c.get("delFlag"), BaseJpaEntity.DEL_NO));

        // 设置查询条件
        Map<String, String> conditionMap = pageParamer.getSearch().getPredicateObject();
        if (conditionMap != null && !conditionMap.isEmpty()) {
            for (Iterator<Map.Entry<String, String>> it = conditionMap.entrySet().iterator(); it.hasNext();) {
                Map.Entry<String, String> entry = it.next();
                // key name, value like,张三
                // key age, value =,10
                // op: like, =

                String key = entry.getKey();
                String value = entry.getValue();

                String[] values = value.split(SPLITER.COMMA);
                String op = ArrayUtils.getValue(values, 0);

                if (op.equals("like")) {
                    whereList.add(cb.like(c.get(key), "%"+value+"%"));
                } else if (op.equals("=")) {
                    whereList.add(cb.equal(c.get(key), value));
                } else if (op.equals("between")) {
                    Path path = c.get(key);
                    if (path.getJavaType().equals(Date.class)) {
                        Date startDate = DateUtils.parseDate(ArrayUtils.getValue(values, 1));
                        Date endDate = DateUtils.parseDate(ArrayUtils.getValue(values, 2));

                        whereList.add(cb.greaterThanOrEqualTo(c.get(key), startDate));
                        whereList.add(cb.lessThanOrEqualTo(c.get(key), endDate));
                    }
                } else {
                    whereList.add(cb.like(c.get(key), "%"+value+"%"));
                }
            }
        }

        q.where(whereList.toArray(new Predicate[whereList.size()]));

        // 设置排序字段
        Order order;
        if (pageParamer.getSort().isReverse()) {
            order = cb.desc(c.get(pageParamer.getSort().getPredicate()));
        } else {
            order = cb.asc(c.get(pageParamer.getSort().getPredicate()));
        }
        q.orderBy(order);

        // 获取当前页数据
        TypedQuery<E> pageQuery = entityManager.createQuery(q)
                .setFirstResult(pageParamer.getPagination().getStart())
                .setMaxResults(pageParamer.getPagination().getNumber());

        // 当前条件的记录数
        TypedQuery<E> countQuery = entityManager.createQuery(q);

        List<E> dataList = afterList(Optional.ofNullable(pageQuery.getResultList()));

        return Optional.ofNullable(new Page<>(pageParamer.getPagination().getStart(), pageParamer.getPagination().getNumber(), countQuery.getResultList().size(), dataList));
    }

    @Transactional
    public Optional<Long> saveOrUpdate(E entity) {
        if (entity.getId() == null) {
            return save(entity);
        } else {
            return Optional.ofNullable(update(entity).get().getId());
        }
    }

    /**
     * 批量修改或者保存集合
     *
     * @param entitySet
     * @return
     */
    @Transactional
    public Optional<Set<Long>> batchSaveOrUpdate(Set<E> entitySet) {
        Set<Long> resultSet = new HashSet<>(entitySet.size());
        entitySet.forEach(entity -> {

            saveOrUpdate(entity);

            resultSet.add(entity.getId());
        });
        return Optional.ofNullable(resultSet);
    }

    @Transactional
    public Optional<Long> save(E entity) {
        entity = beforeSave(entity);

        entity.setDelFlag(E.DEL_NO);
        if (entity.getCreateDate() == null) {
            entity.setCreateDate(new Date());
        }
        entityManager.persist(entity);

        entity = afterSave(entity);
        return Optional.ofNullable(entity.getId());
    }

    /**
     * 批量保存
     *
     * @param entitySet
     * @return
     */
    @Transactional
    public Optional<Set<Long>> batchSave(Set<E> entitySet) {
        Set<Long> resultSet = new HashSet<>(entitySet.size());
        entitySet.forEach(entity -> {

            save(entity);

            resultSet.add(entity.getId());
        });
        return Optional.ofNullable(resultSet);
    }

    @Transactional
    public Optional<E> update(E entity) {
        entity = beforeUpdate(entity);

        if (entity.getUpdateDate() == null) {
            entity.setUpdateDate(new Date());
        }

        entity = afterUpdate(entity);
        return Optional.ofNullable(entityManager.merge(entity));
    }

    /**
     * 批量修改
     *
     * @param entitySet
     * @return
     */
    @Transactional
    public Optional<Set<Long>> batchUpdate(Set<E> entitySet) {
        Set<Long> resultSet = new HashSet<>(entitySet.size());
        entitySet.forEach(entity -> {

            update(entity);

            resultSet.add(entity.getId());
        });
        return Optional.ofNullable(resultSet);
    }

    @Transactional
    public void delete(E entity) {
        entity = beforeDelete(entity);

        entity.setDelFlag(E.DEL_YES);
        update(entity);

        afterDelete(entity);
    }

    @Transactional
    public void delete(Long id) {
        get(id).ifPresent(e -> delete(e));
    }

}
